package com.easylinkin.linkappapi.meterbilling.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.common.service.CommonService;
import com.easylinkin.linkappapi.common.utils.DateUtil;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.meterbilling.entity.EnergyConsumption;
import com.easylinkin.linkappapi.meterbilling.entity.MeterDeviceConfig;
import com.easylinkin.linkappapi.meterbilling.entity.MeterRemedy;
import com.easylinkin.linkappapi.meterbilling.entity.RoomExpensesBill;
import com.easylinkin.linkappapi.meterbilling.entity.ext.MeterRemedyExt;
import com.easylinkin.linkappapi.meterbilling.mapper.MeterDeviceConfigMapper;
import com.easylinkin.linkappapi.meterbilling.mapper.MeterRemedyMapper;
import com.easylinkin.linkappapi.meterbilling.service.MeterRemedyService;
import com.easylinkin.linkappapi.meterbilling.service.RoomExpensesService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.security.entity.LinkappUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * class info :
 *
 * @author liuqihang
 * @date 2021/8/3 15:22
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class MeterRemedyServiceImpl extends ServiceImpl<MeterRemedyMapper, MeterRemedy> implements MeterRemedyService {

    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;

    @Resource
    private CommonService commonService;

    @Resource
    private RoomExpensesService roomExpensesService;

    @Resource
    private MeterDeviceConfigMapper meterDeviceConfigMapper;

    @Override
    public IPage<MeterRemedyExt> getPage(Page page, MeterRemedyExt meterRemedyExt) {
        LinkappUser current = linkappUserContextProducer.getCurrent();
        meterRemedyExt.setTenantId(current.getTenantId());
        return baseMapper.getPage(page, meterRemedyExt);
    }

    @Override
    public String addRemedyRecord(MeterRemedy meterRemedy) throws Exception {
        //封装数据
        LinkappUser current = linkappUserContextProducer.getCurrent();
        meterRemedy.setTenantId(current.getTenantId());
        commonService.setCreateInfo(meterRemedy);
        QueryWrapper qw = new QueryWrapper();
        qw.eq("delete_status","0");
        qw.eq("device_code", meterRemedy.getDeviceCode());
        List<MeterDeviceConfig> meterDeviceConfigList = meterDeviceConfigMapper.selectList(qw);
        if(ObjectUtils.isEmpty(meterDeviceConfigList)){
            throw new Exception("设备编码：" + meterRemedy.getDeviceCode() + "不存在");
        }

        //执行扣费
        String msg = executeRemedyDeduction(meterRemedy, meterRemedy.getCreator(), meterDeviceConfigList);
        //保存记录
        baseMapper.insert(meterRemedy);
        return msg;
    }

    @Override
    public List<MeterRemedy> getNotCalculateMeterRemedyData(String deviceCode){
        QueryWrapper qw = new QueryWrapper();
        qw.eq("calculate_status","0");
        qw.eq("device_code", deviceCode);
        qw.orderByDesc("create_time");
        return baseMapper.selectList(qw);
    }

    @Override
    public void restoreDeviceCalculateStatus(String deviceCode, List<Integer> idList){
        QueryWrapper qw = new QueryWrapper();
        qw.eq("calculate_status", 0);
        if(!StringUtils.isEmpty(deviceCode)){
            qw.eq("device_code", deviceCode);
        }
        if(!ObjectUtils.isEmpty(idList)){
            qw.in("id", idList);
        }
        MeterRemedy mr = new MeterRemedy();
        mr.setCalculateStatus(1);
        baseMapper.update(mr, qw);
    }

    private String executeRemedyDeduction(MeterRemedy meterRemedy, String userId, List<MeterDeviceConfig> meterDeviceConfigList){
        List<MeterRemedy> notCalculateMeterRemedyData = getNotCalculateMeterRemedyData(meterRemedy.getDeviceCode());
        MeterDeviceConfig meterDeviceConfig = meterDeviceConfigList.get(0);
        String msg = null;

        EnergyConsumption ec = new EnergyConsumption();
        if(ObjectUtils.isEmpty(notCalculateMeterRemedyData)){
            //1：不存在旧的抄表记录，用新设备第一次抄表补录计算
            msg = notExistMeterRemedyRecord(ec, meterRemedy, meterDeviceConfigList);
        }else {
            MeterRemedy oldMeterRemedy = notCalculateMeterRemedyData.get(0);
            List<Integer> idList = new ArrayList<>();
            if(oldMeterRemedy.getCreateTime().compareTo(DateUtil.localDateTimeToDate(meterDeviceConfig.getCreateTime())) < 0){
                //2：存在抄表记录，最新抄表记录的时间早于设备绑定时间，执行1
                msg = notExistMeterRemedyRecord(ec, meterRemedy, meterDeviceConfigList);
                idList = notCalculateMeterRemedyData.stream().map(e -> e.getId()).collect(Collectors.toList());
            }
            else {
                //3：存在抄表记录，最新抄表记录的时间晚于设备绑定时间，执行最新填写读表数 减去 上一次补录数据
                ec.setQueryPayTimeStart(oldMeterRemedy.getCreateTime());
                ec.setQueryPayTimeEnd(meterRemedy.getCreateTime());
                ec.setStartRead(oldMeterRemedy.getEnergyAmount());
                ec.setEndRead(meterRemedy.getEnergyAmount());
                ec.setAmount(meterRemedy.getEnergyAmount().subtract(oldMeterRemedy.getEnergyAmount()));

                List<MeterRemedy> meterRemedyList = notCalculateMeterRemedyData.subList(1, notCalculateMeterRemedyData.size());
                idList = meterRemedyList.stream().map(e -> e.getId()).collect(Collectors.toList());
            }
            restoreDeviceCalculateStatus(null, idList);
        }
        ec.setRoomId(meterRemedy.getRoomId());
        ec.setMeterType(meterRemedy.getType());
        ec.setDeviceCode(meterRemedy.getDeviceCode());
        roomExpensesService.calculation(ec, userId,1);
        return msg;
    }

    /**
     * 当做新设备第一次补录处理
     */
    private String asNewDeviceRemedy(EnergyConsumption ec, MeterRemedy meterRemedy, List<MeterDeviceConfig> meterDeviceConfigList){
        Date[] settlementTimePeriod = new Date[2];
        String msg = null;
        settlementTimePeriod[0] = DateUtil.localDateTimeToDate(meterDeviceConfigList.get(0).getCreateTime());
        settlementTimePeriod[1] = meterRemedy.getCreateTime();
        List<EnergyConsumption> energyConsumptionDateList = roomExpensesService.getEnergyConsumptionDateList(settlementTimePeriod, meterDeviceConfigList);
        if(ObjectUtils.isEmpty(energyConsumptionDateList)){
            // 不存在设备流水
            ec.setQueryPayTimeStart(settlementTimePeriod[0]);
            ec.setQueryPayTimeEnd(meterRemedy.getCreateTime());
            ec.setStartRead(meterRemedy.getEnergyAmount());
            ec.setEndRead(meterRemedy.getEnergyAmount());
            ec.setAmount(new BigDecimal(0.0));
            msg = "设备绑定后未产生流水数据，故不产生扣费";
        }else {
            // 存在设备流水
            EnergyConsumption energyConsumption = energyConsumptionDateList.get(0);
            if(energyConsumption.getEndRead() == null){
                ec.setQueryPayTimeStart(settlementTimePeriod[0]);
                ec.setQueryPayTimeEnd(meterRemedy.getCreateTime());
                ec.setStartRead(meterRemedy.getEnergyAmount());
                ec.setEndRead(meterRemedy.getEnergyAmount());
                ec.setAmount(new BigDecimal(0.0));
                msg = "设备绑定后未产生流水数据，故不产生扣费";
            }else {
                ec.setQueryPayTimeStart(energyConsumption.getQueryPayTimeStart());
                ec.setQueryPayTimeEnd(meterRemedy.getCreateTime());
                ec.setStartRead(energyConsumption.getStartRead());
                ec.setEndRead(meterRemedy.getEnergyAmount());
                ec.setAmount(meterRemedy.getEnergyAmount().subtract(energyConsumption.getStartRead()));
            }
        }
        return msg;
    }

    private String notExistMeterRemedyRecord(EnergyConsumption ec, MeterRemedy meterRemedy, List<MeterDeviceConfig> meterDeviceConfigList){
        String msg = null;
        Map<String, List<RoomExpensesBill>> newestRoomExpensesBillRecordByDeviceCode = roomExpensesService.getNewestRoomExpensesBillRecordByDeviceCode(meterRemedy.getDeviceCode());
        if(newestRoomExpensesBillRecordByDeviceCode == null || newestRoomExpensesBillRecordByDeviceCode.isEmpty()){
            msg = asNewDeviceRemedy(ec, meterRemedy, meterDeviceConfigList);
        }
        else {
            //1.2 存在定时扣费记录
            List<RoomExpensesBill> roomExpensesBillList = newestRoomExpensesBillRecordByDeviceCode.get(meterRemedy.getDeviceCode());

            if(ObjectUtils.isEmpty(roomExpensesBillList)){
                //1.2.1 存在定时扣费记录，但无此设备的扣费记录
                msg = asNewDeviceRemedy(ec, meterRemedy, meterDeviceConfigList);
            }
            else {
                RoomExpensesBill roomExpensesBill = roomExpensesBillList.get(0);
                if(roomExpensesBill.getCreateTime().compareTo(DateUtil.localDateTimeToDate(meterDeviceConfigList.get(0).getCreateTime())) < 0){
                    msg = asNewDeviceRemedy(ec, meterRemedy, meterDeviceConfigList);
                }
                else {
                    ec.setQueryPayTimeStart(roomExpensesBill.getSettlementTimeEnd());
                    ec.setQueryPayTimeEnd(meterRemedy.getCreateTime());
                    ec.setStartRead(roomExpensesBill.getEndReadAmount());
                    ec.setEndRead(meterRemedy.getEnergyAmount());
                    ec.setAmount(meterRemedy.getEnergyAmount().subtract(roomExpensesBill.getEndReadAmount()));
                }
            }
        }
        return msg;
    }

    @Override
    public void export(MeterRemedyExt meterRemedyExt, HttpServletRequest request, HttpServletResponse response) {
        LinkappUser current = linkappUserContextProducer.getCurrent();
        meterRemedyExt.setTenantId(current.getTenantId());
        List<MeterRemedyExt> meterRemedyExtList = baseMapper.getPage(meterRemedyExt);
        meterRemedyExtList.forEach(mre -> {
            if(mre.getMeterType().equals(0)){
                mre.setMeterTypeName("水表");
            }else if(mre.getMeterType().equals(1)){
                mre.setMeterTypeName("电表");
            }
        });

        Assert.notEmpty(meterRemedyExtList,"无数据");

        String keyValue = "房间:roomName,设备类型:meterTypeName,设备名称:deviceName,设备编码:deviceCode,补录止码:energyAmount,备注:remark,时间:remedyTime,操作人:creatorName";
        String title = "抄表补录";
        String fileName = title + ".xls";
        try {
            List<String> stringList = new ArrayList<String>();
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportBankExcelV2(outputStream, keyValue, meterRemedyExtList, ExcelConstant.XLS, title, stringList);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            log.error("excel导出失败");
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            log.error("excel导出失败");
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }

    }
}
